package com.ghp.admin.controller;

import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ghp.admin.annotation.AuthCheck;
import com.ghp.admin.service.UserService;
import com.ghp.auth.model.AuthCallback;
import com.ghp.auth.model.AuthUser;
import com.ghp.auth.request.AuthRequest;
import com.ghp.auth.request.AuthRequestFactory;
import com.ghp.auth.utils.AuthUuidUtils;
import com.ghp.common.model.dto.DeleteIdDTO;
import com.ghp.common.model.dto.QueryIdDTO;
import com.ghp.common.model.dto.user.*;
import com.ghp.common.model.entity.User;
import com.ghp.common.model.vo.UserLoginVO;
import com.ghp.common.model.vo.UserVO;
import com.ghp.common.utils.*;
import com.ghp.common.utils.response.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.ghp.common.constants.ErrorMessageConstants.PARAMS_NOT_NULL;
import static com.ghp.common.constants.ErrorMessageConstants.REQUEST_STATE_INCONSISTENCY;
import static com.ghp.common.constants.RedisConstants.*;
import static com.ghp.common.constants.UserConstants.ROLE_ADMIN;
import static com.ghp.common.utils.response.ResponseStatus.*;

/**
 * @author ghp
 * @title
 * @description
 */
@Api
@RestController
@RequestMapping("/user")
@Slf4j
@Validated
public class UserController {

    @Resource
    private UserService userService;

    @Resource
    private RedisTemplate redisTemplate;

    // region 登录注册相关

    @PostMapping("/register")
    @ApiOperation(value = "用户注册")
    public ResponseResult userRegister(@Valid @RequestBody UserRegisterDTO userRegisterDTO) {
        return userService.userRegister(userRegisterDTO);
    }

    @PostMapping("/forget")
    @ApiOperation(value = "忘记密码")
    public ResponseResult userForgetPassword(@Valid @RequestBody UserRegisterDTO userRegisterDTO) {
        return userService.userForgetPassword(userRegisterDTO);
    }

    @PostMapping("/login")
    @ApiOperation(value = "用户登录")
    public ResponseResult<UserLoginVO> userLogin(@Valid @RequestBody UserLoginDTO userLoginDTO) {
        return userService.userLogin(userLoginDTO);
    }

    @PostMapping("/login/{source}")
    @ApiOperation(value = "第三方登录")
    public ResponseResult<String> userAuthLogin(@PathVariable("source") String source) {
        ThrowUtils.throwIf(StringUtils.isBlank(source), PARAMS_NOT_NULL);
        AuthRequest authRequest = AuthRequestFactory.getInstance().buildAuthRequest(source);
        // 生成一个随机数，用于防止 CSRF 攻击
        String state = AuthUuidUtils.getUUID();
        String authorizeUrl = authRequest.authorize(state);
        redisTemplate.opsForValue().setIfAbsent(LOGIN_USER_STATE_KEY + state, "",
                Duration.ofSeconds(LOGIN_USER_STATE_KEY_TTL));
        return ResponseResult.success(authorizeUrl);
    }

    @GetMapping("/login/callback/{source}")
    @ApiOperation(value = "第三方登录回调校验")
    public void userAuthCallbackLogin(@PathVariable("source") String source, AuthCallback callback,
                                      HttpServletResponse response) throws IOException {
        // 安全性校验
        ThrowUtils.throwIf(StringUtils.isBlank(source) || callback == null, PARAMS_NOT_NULL);
        Boolean result = redisTemplate.opsForValue().setIfAbsent(LOGIN_USER_STATE_KEY + callback.getState(), "",
                Duration.ofMillis(1));
        // 如果发现能够为 state 设置值，说明 state 已过期或者是遭到了篡改
        ThrowUtils.throwIf(Boolean.TRUE.equals(result), REQUEST_STATE_INCONSISTENCY);

        // 判断是否授权成功
        AuthRequest authRequest = AuthRequestFactory.getInstance().buildAuthRequest(source);
        AuthUser authUser = authRequest.login(callback);
        if (authUser == null) {
            WebResponseUtils.renderString(response, "授权登录失败");
        }
        // 用户已成功登录，判断用户是否已存在账号
        String account = source + "_" + authUser.getUsername();
        User one = userService.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, account));
        if (one == null) {
            // 用户不存在，则进行登录
            one = new User();
            String password = UUID.fastUUID().toString(true);
            String accessKey = KeyGeneratorUtils.generateAccessKey();
            String secretKey = KeyGeneratorUtils.generateSecretKey();
            one.setUserAccount(account);
            String nickName = authUser.getNickname();
            if (StringUtils.isBlank(nickName) || "null".equals(nickName)) {
                // 防止NPE，Gitee 有昵称，但是 Github 没有昵称
                nickName = UserNameGeneratorUtils.generateUsername();
            }
            one.setUserName(nickName);
            one.setUserAvatar(authUser.getAvatar());
            one.setUserPassword(password);
            one.setAccessKey(accessKey);
            one.setSecretKey(secretKey);
            boolean isSuccess = userService.save(one);
            ThrowUtils.throwIf(!isSuccess, SERVICE_INSERT_ERROR);
        }
        // 生成token，并存储登录用户的信息
        Long userId = one.getId();
        ThreadLocalUtils.setCurrentId(userId);
        UserLoginVO userLoginVO = BeanConvertorUtils.copyBean(one, UserLoginVO.class);
        String token = JwtGeneratorUtils.createJWT(userId.toString());
        userLoginVO.setToken(token);
        Map<String, Object> userMap = BeanConvertorUtils.beanToMap(userLoginVO);
        String key = LOGIN_USER_KEY + userId;
        redisTemplate.opsForHash().putAll(key, userMap);
        redisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.SECONDS);
        // TODO dev 和 prod 环境需要切换
//        response.sendRedirect("http://localhost:8000/?token=" + token);
        response.sendRedirect("http://api-hub.xn--torp68ch8h18h1jp.top/?token=" + token);
    }

    @PostMapping("/logout")
    @ApiOperation(value = "用户退出")
    public ResponseResult userLogout() {
        // 删除缓存即可
        Long userId = ThreadLocalUtils.getCurrentId();
        redisTemplate.delete(LOGIN_USER_KEY + userId);
        ThreadLocalUtils.removeCurrentId();
        return ResponseResult.success();
    }

    @GetMapping("/get/login")
    @ApiOperation(value = "获取当前登录用户信息")
    public ResponseResult<UserLoginVO> getLoginUser() {
        UserLoginVO userLoginVO = userService.getUserLoginVO(ThreadLocalUtils.getCurrentId());
        return ResponseResult.success(userLoginVO);
    }

    // endregion


    // region 增删改查

    @PostMapping("/add")
    @ApiOperation(value = "新增用户")
    @AuthCheck(mustRole = ROLE_ADMIN)
    public ResponseResult addUser(@Valid @RequestBody UserAddDTO userAddDTO) {
        boolean isSuccess = userService.save(BeanConvertorUtils.copyBean(userAddDTO, User.class));
        ThrowUtils.throwIf(!isSuccess, SERVICE_INSERT_ERROR);
        return ResponseResult.success();
    }

    @PostMapping("/delete")
    @ApiOperation(value = "删除用户")
    @AuthCheck(mustRole = ROLE_ADMIN)
    public ResponseResult<Boolean> deleteUser(@Valid @RequestBody DeleteIdDTO deleteIdDTO) {
        boolean isSuccess = userService.removeById(deleteIdDTO.getId());
        ThrowUtils.throwIf(!isSuccess, SERVICE_DELETE_ERROR);
        return ResponseResult.success();
    }

    /**
     * 更新用户
     *
     * @param userUpdateDTO
     * @return
     */
    @PostMapping("/update")
    @ApiOperation(value = "更新用户")
    @AuthCheck(mustRole = ROLE_ADMIN)
    public ResponseResult<Boolean> updateUser(@Valid @RequestBody UserUpdateDTO userUpdateDTO) {
        boolean isSuccess = userService.updateById(BeanConvertorUtils.copyBean(userUpdateDTO, User.class));
        ThrowUtils.throwIf(!isSuccess, SERVICE_UPDATE_ERROR);
        return ResponseResult.success();
    }

    /**
     * 根据 id 获取用户（仅管理员）
     *
     * @param queryIdDTO
     * @return
     */
    @PostMapping("/get")
    @ApiOperation(value = "根据id查询详细用户")
    @AuthCheck(mustRole = ROLE_ADMIN)
    public ResponseResult<User> getUserById(@Valid @RequestBody QueryIdDTO queryIdDTO) {
        User user = userService.getById(queryIdDTO.getId());
        ThrowUtils.throwIf(Objects.isNull(user), SUCCESS_NOT_EXISTS);
        return ResponseResult.success(user);
    }

    @PostMapping("/get/vo")
    @ApiOperation(value = "根据id查询脱敏用户")
    public ResponseResult<UserVO> getUserVOById(@Valid @RequestBody QueryIdDTO queryIdDTO) {
        ResponseResult<User> responseResult = this.getUserById(queryIdDTO);
        User user = responseResult.getData();
        return ResponseResult.success(BeanConvertorUtils.copyBean(user, UserVO.class));
    }

    @PostMapping("/list/page")
    @ApiOperation(value = "分页详细查询")
    @AuthCheck(mustRole = ROLE_ADMIN)
    public ResponseResult<Page<User>> listUserByPage(@Valid @RequestBody UserQueryDTO userQueryDTO) {
        return userService.listUserByPage(userQueryDTO);
    }

    @PostMapping("/list/page/vo")
    @ApiOperation(value = "分页脱敏查询")
    public ResponseResult<Page<UserVO>> listUserVOByPage(@Valid @RequestBody UserQueryDTO userQueryDTO) {
        return userService.listUserVOByPage(userQueryDTO);
    }

    @PostMapping("/update/my")
    @ApiOperation(value = "更新个人信息")
    public ResponseResult<Boolean> updateOwner(@Valid @RequestBody UserUpdateOwnerDTO userUpdateOwnerDTO) {
        return userService.updateOwner(userUpdateOwnerDTO);
    }

    // endregion

    @PostMapping("/update/secretKey")
    @ApiOperation(value = "更新用户私钥")
    public ResponseResult<Boolean> updateSecretKey(@Valid @RequestBody QueryIdDTO queryIdDTO) {
        // TODO 更新用户私钥
        return ResponseResult.success("功能尚未开发");
    }

}

